package com.wflow.workflow.service.impl;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wflow.bean.entity.WflowModelHistorys;
import com.wflow.exception.BusinessException;
import com.wflow.mapper.WflowModelHistorysMapper;
import com.wflow.service.OrgRepositoryService;
import com.wflow.utils.UserUtil;
import com.wflow.workflow.bean.dto.NotifyDto;
import com.wflow.workflow.bean.dto.ProcessInstanceOwnerDto;
import com.wflow.workflow.bean.process.OrgUser;
import com.wflow.workflow.bean.process.props.ApprovalProps;
import com.wflow.workflow.bean.process.props.CcProps;
import com.wflow.workflow.bean.vo.*;
import com.wflow.workflow.config.WflowGlobalVarDef;
import com.wflow.workflow.extension.cmd.RecallToHisApprovalNodeCmd;
import com.wflow.workflow.service.FormService;
import com.wflow.workflow.service.NotifyService;
import com.wflow.workflow.service.ProcessTaskService;
import com.wflow.workflow.service.UserDeptOrLeaderService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.*;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.ExecutionGraphUtil;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.history.NativeHistoricTaskInstanceQuery;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : willian fu
 * @date : 2022/8/25
 */
@Slf4j
@Service("processTaskService")
public class ProcessTaskServiceImpl implements ProcessTaskService {

    @Autowired
    private TaskService taskService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private NotifyService notifyService;

    @Autowired
    private FormService formService;

    @Autowired
    private UserDeptOrLeaderService userDeptOrLeaderService;

    @Autowired
    private OrgRepositoryService orgRepositoryService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ManagementService managementService;

    @Autowired
    private WflowModelHistorysMapper historysMapper;

    //超时缓存，数据缓存20秒，用来存储审批人防止flowable高频调用
    private static final TimedCache<String, List<String>> taskCache = CacheUtil.newTimedCache(20000);

    static {
        taskCache.schedulePrune(10000);
    }

    @Override
    public Page<ProcessTaskVo> getUserTodoList(Integer pageSize, Integer pageNo, String code) {
        String userId = UserUtil.getLoginUserId();
        TaskQuery taskQuery = taskService.createTaskQuery();
        if (StrUtil.isNotBlank(code)) {
            taskQuery.processDefinitionKey(code);
        }
        taskQuery.active().taskCandidateOrAssigned(userId).orderByTaskCreateTime().desc();
        Page<ProcessTaskVo> page = new Page<>();
        List<Task> taskList = taskQuery.listPage(pageSize * (pageNo - 1), pageSize);
        page.setTotal(taskQuery.count());
        page.setCurrent(pageNo);
        page.setSize(pageSize);
        page.setRecords(Collections.emptyList());
        Set<String> staterUsers = new HashSet<>();
        List<ProcessTaskVo> taskVos = taskList.stream().map(task -> {
            ProcessDefinition processDef = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(task.getProcessDefinitionId())
                    .singleResult();
            //从缓存取
            ProcessInstanceOwnerDto owner = runtimeService.getVariable(task.getProcessInstanceId(), "owner", ProcessInstanceOwnerDto.class);
            if (Objects.isNull(owner)){
                owner = ProcessInstanceOwnerDto.builder()
                        .owner("")
                        .ownerDeptName("未知")
                        .ownerDeptId("")
                        .ownerName("未知").build();
            }
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            staterUsers.add(owner.getOwner());
            return ProcessTaskVo.builder()
                    .taskId(task.getId())
                    .taskName(task.getName())
                    .taskDefKey(task.getTaskDefinitionKey())
                    .processDefId(task.getProcessDefinitionId())
                    .executionId(task.getExecutionId())
                    .nodeId(task.getTaskDefinitionKey())
                    .deployId(processDef.getDeploymentId())
                    .processDefName(processDef.getName())
                    .version(processDef.getVersion())
                    .instanceId(task.getProcessInstanceId())
                    .ownerId(processInstance.getStartUserId())
                    .ownerDeptId(owner.getOwnerDeptId())
                    .ownerDeptName(owner.getOwnerDeptName())
                    .createTime(processInstance.getStartTime())
                    .taskCreateTime(task.getCreateTime())
                    .build();
        }).collect(Collectors.toList());
        //取用户信息，减少数据库查询，一次构建
        if (CollectionUtil.isNotEmpty(staterUsers)) {
            Map<String, OrgUser> userMap = userDeptOrLeaderService.getUserMapByIds(staterUsers);
            page.setRecords(taskVos.stream().peek(v -> v.setOwner(userMap.get(v.getOwnerId()))).collect(Collectors.toList()));
        }
        return page;
    }

    @Override
    public Page<ProcessTaskVo> getUserIdoList(Integer pageSize, Integer pageNo, String code) {
        String userId = UserUtil.getLoginUserId();
        //自定义sql查询所有已办的任务
        String nativeSql = StrUtil.builder("SELECT aht.* FROM `ACT_HI_TASKINST` AS aht ")
                .append("LEFT JOIN `ACT_HI_VARINST` AS ahv ON SUBSTRING(ahv.NAME_, 9) = aht.ID_ AND ahv.NAME_ LIKE 'approve_%'")
                .append("LEFT JOIN `ACT_RE_PROCDEF` AS arp ON arp.ID_ = aht.PROC_DEF_ID_ ")
                .append("WHERE aht.ASSIGNEE_ = '").append(userId).append("' ")
                .append(StrUtil.isNotBlank(code) ? (" AND arp.KEY_ = '" + code + "' ") : " ")
                .append("AND ahv.NAME_ IS NOT NULL ORDER BY aht.END_TIME_ DESC").toString();
        NativeHistoricTaskInstanceQuery countQuery = historyService.createNativeHistoricTaskInstanceQuery().sql("SELECT COUNT(0) FROM (" + nativeSql + ") AS ct");
        NativeHistoricTaskInstanceQuery taskInstanceQuery = historyService.createNativeHistoricTaskInstanceQuery().sql(nativeSql);
        List<HistoricTaskInstance> taskInstances = taskInstanceQuery.listPage(pageSize * (pageNo - 1), pageSize);
        //把已办任务流程实例一次性取出来，减少查询次数
        Map<String, HistoricProcessInstance> instanceMap = CollectionUtil.isNotEmpty(taskInstances) ?
                historyService.createHistoricProcessInstanceQuery().processInstanceIds(taskInstances.stream()
                                .map(HistoricTaskInstance::getProcessInstanceId).collect(Collectors.toSet()))
                        .list().stream().collect(Collectors.toMap(HistoricProcessInstance::getId, v -> v)) : new HashMap<>();
        Page<ProcessTaskVo> page = new Page<>();
        page.setTotal(countQuery.count());
        page.setCurrent(pageNo);
        page.setSize(pageSize);
        Set<String> staterUsers = new HashSet<>();
        List<ProcessTaskVo> taskVos = taskInstances.stream().map(task -> {
            HistoricProcessInstance instance = instanceMap.get(task.getProcessInstanceId());
            HistoricVariableInstance variableInstance = historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(instance.getId()).variableName("approve_" + task.getId()).singleResult();
            //从缓存取
            staterUsers.add(instance.getStartUserId());
            return ProcessTaskVo.builder()
                    .taskId(task.getId())
                    .taskName(task.getName())
                    .taskDefKey(task.getTaskDefinitionKey())
                    .processDefId(task.getProcessDefinitionId())
                    .executionId(task.getExecutionId())
                    .nodeId(task.getTaskDefinitionKey())
                    .deployId(instance.getDeploymentId())
                    .processDefName(instance.getProcessDefinitionName())
                    .version(instance.getProcessDefinitionVersion())
                    .instanceId(task.getProcessInstanceId())
                    .ownerId(instance.getStartUserId())
                    .createTime(instance.getStartTime())
                    .taskCreateTime(task.getCreateTime())
                    .taskEndTime(task.getEndTime())
                    .taskResult(String.valueOf(Objects.nonNull(variableInstance) ?
                            variableInstance.getValue() : ProcessHandlerParamsVo.Action.recall))
                    .build();
        }).collect(Collectors.toList());
        //取用户信息，减少数据库查询，一次构建
        if (CollectionUtil.isNotEmpty(staterUsers)) {
            Map<String, OrgUser> userMap = userDeptOrLeaderService.getUserMapByIds(staterUsers);
            page.setRecords(taskVos.stream().peek(v -> v.setOwner(userMap.get(v.getOwnerId()))).collect(Collectors.toList()));
        }
        return page;
    }

    @Override
    @Transactional
    public void approvalTask(ProcessHandlerParamsVo params) {
        String userId = UserUtil.getLoginUserId();
        Authentication.setAuthenticatedUserId(userId);
        Task task = null;
        if (StrUtil.isNotBlank(params.getTaskId())){
            task = taskService.createTaskQuery().taskId(params.getTaskId()).active().singleResult();
        }
        if (hasComment(params.getComment())) {
            boolean putComment = Objects.nonNull(task) && !userId.equals(task.getAssignee());
            boolean isComment = ProcessHandlerParamsVo.Action.comment.equals(params.getAction());
            if (ProcessHandlerParamsVo.Action.cancel.equals(params.getAction())){
                params.getComment().setText("撤销：" + params.getComment().getText());
            }
            taskService.addComment(
                    isComment || putComment ? null : params.getTaskId(), params.getInstanceId(),
                    JSONObject.toJSONString(params.getComment()));
        }
        if (StrUtil.isNotBlank(params.getSignature())){
            if (params.getUpdateSign()){
                orgRepositoryService.updateUserSign(userId, params.getSignature());
            }
        }

        if (!ProcessHandlerParamsVo.Action.comment.equals(params.getAction())) {
            if (ObjectUtil.isNull(task) && !ProcessHandlerParamsVo.Action.cancel.equals(params.getAction())) {
                throw new BusinessException("任务不存在");
            }else if (ObjectUtil.isNotNull(task) && "root".equals(task.getTaskDefinitionKey())
                    && !(ProcessHandlerParamsVo.Action.agree.equals(params.getAction())
                        || ProcessHandlerParamsVo.Action.cancel.equals(params.getAction()))){
                throw new BusinessException("发起人节点不支持此项操作");
            }
            switch (params.getAction()) {
                case agree:
                case refuse:
                    formService.updateInstanceFormData(params.getInstanceId(), params.getFormData());
                    doApproval(task, params);
                    break;
                case recall: //退回
                    doRecallTask(task, userId, params);
                    break;
                case transfer: //转交
                    formService.updateInstanceFormData(params.getInstanceId(), params.getFormData());
                    doTransferTask(task, params, userId);
                    break;
                case afterAdd:
                case beforeAdd: //加签，暂时只支持后加签
                    //加签如果是顺序会签，暂时先禁止，
                    BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
                    UserTask userTask = (UserTask) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
                    MultiInstanceLoopCharacteristics loopCharacteristics = userTask.getLoopCharacteristics();
                    if (Objects.isNull(loopCharacteristics) || loopCharacteristics.isSequential()){
                        throw new BusinessException("当前节点不支持临时加签");
                    }
                    runtimeService.addMultiInstanceExecution(task.getTaskDefinitionKey(),
                            task.getProcessInstanceId(), Collections.singletonMap("assignee", params.getTargetUser()));
                    log.info("[{}]将任务[{}]加签给[{}]处理", userId, task.getId(), params.getTargetUser());
                    break;
                case cancel:
                    List<ActivityInstance> instances = runtimeService.createActivityInstanceQuery()
                            .processInstanceId(params.getInstanceId()).unfinished().list();
                    doCancelProcess(params.getInstanceId(), instances.get(0).getActivityId());
                    break;
                case comment:
                    //throw new BusinessException("暂不支持单独评论");
                    break;
            }
        }
        Authentication.setAuthenticatedUserId(null);
    }

    @Override
    public Set<String> getCcTaskUsers(DelegateExecution execution, String instance, String nodeId) {
        //获取设置项
        HistoricVariableInstance variableInstance = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(instance)
                .variableName(WflowGlobalVarDef.WFLOW_NODE_PROPS).singleResult();
        Map nodeProps;
        if (Objects.nonNull(variableInstance)) {
            nodeProps = (Map) variableInstance.getValue();
        } else {
            //流程首个节点需要从执行实例中取数据
            nodeProps = execution.getVariable(WflowGlobalVarDef.WFLOW_NODE_PROPS, Map.class);
        }
        CcProps ccProps = (CcProps) nodeProps.get(nodeId);
        //获取变量里面自选的抄送人
        Set<String> ccUsers = new HashSet<>();
        List<OrgUser> orgs = new ArrayList<>(ccProps.getAssignedUser());
        if (ccProps.getShouldAdd()) {
            //获取发起流程时添加的抄送人
            HistoricVariableInstance result = historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(instance)
                    .variableName(nodeId).singleResult();
            if (Objects.nonNull(result)) {
                Optional.ofNullable(result.getValue()).ifPresent(us -> orgs.addAll((List<OrgUser>) us));
            } else {
                Optional.ofNullable(execution.getVariable(nodeId, List.class)).ifPresent(orgs::addAll);
            }
        }
        //解析部门与人员选项
        ccUsers.addAll(orgs.stream().filter(org -> "user".equals(org.getType()))
                .map(OrgUser::getId).collect(Collectors.toSet()));
        ccUsers.addAll(userDeptOrLeaderService.getUsersByDept(orgs.stream()
                .filter(org -> "dept".equals(org.getType()))
                .map(OrgUser::getId).collect(Collectors.toSet())));
        return ccUsers;
    }

    @Override
    public List<String> getNodeApprovalUsers(ExecutionEntity execution) {
        //取缓存里面的，判断之前有没有，多实例防止多次解析
        List<String> cacheUsers = taskCache.get(execution.getProcessInstanceId() + execution.getActivityId());
        if (Objects.nonNull(cacheUsers)){
            return cacheUsers;
        }
        log.info("获取节点[{}]的审批人", execution.getActivityId());
        Map propsMap = execution.getVariable(WflowGlobalVarDef.WFLOW_NODE_PROPS, Map.class);
        ApprovalProps props = (ApprovalProps) propsMap.get(execution.getActivityId());
        List<String> approvalUsers = getApprovalUsers(execution, props);
        taskCache.put(execution.getProcessInstanceId() + execution.getActivityId(), approvalUsers);
        return approvalUsers;
    }

    @Override
    public List<ProcessProgressVo.ProgressNode> getFutureTask(String instanceId) {
        //根据流程遍历后续节点，期间要穿越后续包含并行网关和条件网关的节点
        return Collections.emptyList();
    }

    @Override
    public List<HisApprovalNodeVo> getRecallTaskNodes(String instanceId, String taskId) {
        ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        //获取当前用户的所有待审批任务
        String userId = UserUtil.getLoginUserId();
        Task task = taskService.createTaskQuery().processInstanceId(instanceId).taskCandidateOrAssigned(userId).taskId(taskId).active().singleResult();
        if (Objects.isNull(task)) {
            throw new BusinessException("该任务不存在");
        }
        //获取已经处理过的任务
        List<ActivityInstance> activityInstances = runtimeService.createActivityInstanceQuery()
                .processInstanceId(instanceId).finished().orderByActivityInstanceStartTime().asc().list();
        Map<String, String> collect = activityInstances.stream()
                .filter(act -> BpmnXMLConstants.ELEMENT_TASK_USER.equals(act.getActivityType()))
                .collect(Collectors.toMap(ActivityInstance::getActivityId, ActivityInstance::getActivityName, (v1, v2) -> v2));
        //移除当前的自身节点
        collect.remove(task.getTaskDefinitionKey());
        if (CollectionUtil.isNotEmpty(collect)) {
            BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
            //获取当前节点
            FlowNode nowNode = (FlowNode)bpmnModel.getFlowElement(task.getTaskDefinitionKey());
            Set<String> hisNodes = new HashSet<>();
            loadBeforeSerialUserTaskNode(nowNode, hisNodes);
            //取交集
            Collection<String> intersection = new HashSet<>(CollectionUtil.intersection(collect.keySet(), hisNodes));
            //获取任务所在审批节点
            return collect.keySet().stream().filter(key -> {
                        FlowNode sflowNode = (FlowNode) bpmnModel.getFlowElement(key);
                        FlowNode tflowNode = (FlowNode) bpmnModel.getFlowElement(task.getTaskDefinitionKey());
                        return ExecutionGraphUtil.isReachable(bpmnModel.getMainProcess(), sflowNode, tflowNode, new HashSet<>())
                                && intersection.contains(key);
                    }).map(key -> new HisApprovalNodeVo(key, collect.getOrDefault(key, "--")))
                    .collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    @Override
    public NodeSettingsVo getNodeTaskSettings(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (Objects.isNull(task)){
            throw new BusinessException("该任务已被处理，请刷新数据");
        }
        WflowModelHistorys modelHistory = historysMapper.selectOne(
                new LambdaQueryWrapper<WflowModelHistorys>()
                .eq(WflowModelHistorys::getProcessDefId, task.getProcessDefinitionId()));
        JSONObject object = JSONObject.parseObject(modelHistory.getSettings());
        Boolean sign = object.getBoolean("sign");
        //先查全局设置，如果没开启签名，就再查流程节点设置
        if (Boolean.TRUE.equals(sign)) {
            return NodeSettingsVo.builder().enableSign(true).build();
        }
        Map variable = (Map)runtimeService.getVariable(task.getProcessInstanceId(), WflowGlobalVarDef.WFLOW_NODE_PROPS);
        Object nodeProps = variable.get(task.getTaskDefinitionKey());
        return NodeSettingsVo.builder().enableSign(nodeProps instanceof ApprovalProps && ((ApprovalProps)nodeProps).isSign()).build();
    }

    /**
     * 获取审批人
     *
     * @param execution 上下文
     * @param props     节点熟悉
     * @return 审批人ID列表
     */
    public List<String> getApprovalUsers(ExecutionEntity execution, ApprovalProps props) {
        Set<String> userSet = new LinkedHashSet<>();
        switch (props.getAssignedType()) {
            case REFUSE:
                userSet.add(WflowGlobalVarDef.WFLOW_TASK_REFUSE);
                break;
            case SELF: //取流程发起人
                ProcessInstanceOwnerDto owner = execution.getVariable("owner", ProcessInstanceOwnerDto.class);
                Optional.ofNullable(owner).ifPresent(on -> userSet.add(on.getOwner()));
                break;
            case ROLE: //取角色
                userSet.addAll(userDeptOrLeaderService.getUsersByRoles(props.getRole().stream().map(OrgUser::getId).collect(Collectors.toList())));
                break;
            case FORM_USER: //从表单取
                List<Map<String, Object>> userList = execution.getVariable(props.getFormUser(), List.class);
                Optional.ofNullable(userList).ifPresent(users -> {
                    userSet.addAll(users.stream().map(u -> u.get("id").toString()).collect(Collectors.toList()));
                });
                break;
            case FORM_DEPT: //从表单取
                List<Map<String, Object>> deptList = execution.getVariable(props.getFormDept(), List.class);
                Optional.ofNullable(deptList).ifPresent(users -> {
                    userSet.addAll(userDeptOrLeaderService.getLeadersByDept(
                            users.stream().map(u -> u.get("id").toString()).collect(Collectors.toList())));
                });
                break;
            case ASSIGN_USER://指定用户
                userSet.addAll(props.getAssignedUser().stream().map(OrgUser::getId).collect(Collectors.toList()));
                break;
            case ASSIGN_LEADER:
                List<String> collect = props.getAssignedDept().stream().map(OrgUser::getId).collect(Collectors.toList());
                userSet.addAll(userDeptOrLeaderService.getLeadersByDept(collect));
                break;
            case SELF_SELECT: //自选用户，从变量取，这一步在发起流程时设置的
                List<OrgUser> selectUsers = execution.getVariable(execution.getActivityId(), List.class);
                Optional.ofNullable(selectUsers).ifPresent(on -> userSet.addAll(on.stream().map(OrgUser::getId).collect(Collectors.toList())));
                break;
            case LEADER: //用户的指定级别部门主管
                ProcessInstanceOwnerDto owner2 = execution.getVariable("owner", ProcessInstanceOwnerDto.class);
                String leaderByLevel = userDeptOrLeaderService.getUserLeaderByLevel(owner2.getOwner(),
                        owner2.getOwnerDeptId(), props.getLeader().getLevel(), props.getLeader().getSkipEmpty());
                Optional.ofNullable(leaderByLevel).ifPresent(userSet::add);
                break;
            case LEADER_TOP: //用户逐级部门主管
                ProcessInstanceOwnerDto owner3 = execution.getVariable("owner", ProcessInstanceOwnerDto.class);
                List<String> leaders = userDeptOrLeaderService.getUserLeadersByLevel(owner3.getOwner(),
                        owner3.getOwnerDeptId(), "TOP".equals(props.getLeaderTop().getEndCondition()) ?
                                0 : props.getLeaderTop().getEndLevel(), props.getLeaderTop().getSkipEmpty());
                Optional.ofNullable(leaders).ifPresent(userSet::addAll);
                break;
        }
        //处理审批人为空时，采取默认策略
        if (CollectionUtil.isEmpty(userSet)) {
            switch (props.getNobody().getHandler()) {
                case TO_USER:
                    userSet.addAll(props.getNobody().getAssignedUser().stream().map(OrgUser::getId).collect(Collectors.toList()));
                    break;
                case TO_ADMIN: //TODO 注意系统需要包含该角色 WFLOW_APPROVAL_ADMIN
                    userSet.addAll(userDeptOrLeaderService.getUsersByRoles(CollectionUtil.newArrayList(WflowGlobalVarDef.WFLOW_APPROVAL_ADMIN)));
                    break;
                case TO_PASS:
                    userSet.add(WflowGlobalVarDef.WFLOW_TASK_AGRRE);
                    break;
                case TO_REFUSE:
                    userSet.add(WflowGlobalVarDef.WFLOW_TASK_REFUSE);
                    break;
            }
        } else {
            //将用户替换为当前代理人
            return userDeptOrLeaderService.replaceUserAsAgent(userSet);
        }
        return new ArrayList<>(userSet);
    }

    private boolean hasComment(ProcessHandlerParamsVo.ProcessComment comment) {
        return Objects.nonNull(comment) && (StrUtil.isNotBlank(comment.getText())
                || CollectionUtil.isNotEmpty(comment.getAttachments()));
    }

    /**
     * 审批任务，同意、驳回
     *
     * @param task   当前任务
     * @param params 参数
     */
    private void doApproval(Task task, ProcessHandlerParamsVo params) {
        HashMap<String, Object> var = CollectionUtil.newHashMap(2);
        //var.put("approve", params.getAction().equals(ProcessHandlerParamsVo.Action.agree));
        var.put("approve_" + task.getId(), params.getAction());
        if (StrUtil.isNotBlank(params.getSignature())){
            var.put("sign_" + task.getId(), params.getSignature());
        }
        taskService.complete(params.getTaskId(), var);
    }

    private void doRecallTask(Task task, String userId, ProcessHandlerParamsVo params) {
        //执行自定义回退逻辑
        HashMap<String, Object> var = CollectionUtil.newHashMap(1);
        var.put("approve_" + params.getTaskId(), params.getAction());
        managementService.executeCommand(new RecallToHisApprovalNodeCmd(runtimeService, params.getTaskId(), params.getTargetNode()));
        runtimeService.setVariables(params.getInstanceId(), var);
        if ("root".equals(params.getTargetNode())){
            //如果是退回发起人节点，那么推送一条通知消息，因为发起人节点没有设置任务监听器，所以不会触发UserTaskListener
            ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            notifyService.notify(NotifyDto.builder()
                    .title("您的审批被退回")
                    .instanceId(task.getProcessInstanceId())
                    .target(instance.getStartUserId())
                    .processDefId(task.getProcessDefinitionId())
                    .content(StrUtil.builder("您提交的【", instance.getProcessDefinitionName(),
                            "】被退回，请即时处理").toString())
                    .type(NotifyDto.TypeEnum.WARNING)
                    .build());
        }
        log.info("用户[{}] 退回流程[{}] [{} -> {}]", userId, params.getInstanceId(), task.getTaskDefinitionKey(), params.getTargetNode());
    }

    /**
     * 转交任务处理
     *
     * @param task   当前任务
     * @param params 参数
     * @param userId 任务当前处理人
     */
    private void doTransferTask(Task task, ProcessHandlerParamsVo params, String userId) {
        taskService.setOwner(params.getTaskId(), userId);
        taskService.setAssignee(params.getTaskId(), params.getTargetUser());
        OrgUser orgUser = userDeptOrLeaderService.getUserMapByIds(CollectionUtil.newArrayList(userId)).get(userId);
        notifyService.notify(NotifyDto.builder()
                .title("待处理的转交任务")
                .instanceId(task.getProcessInstanceId())
                .target(params.getTargetUser())
                .processDefId(task.getProcessDefinitionId())
                .content(StrUtil.builder(orgUser.getName(),
                        "转交了一项任务给您处理").toString())
                .type(NotifyDto.TypeEnum.WARNING)
                .build());
        log.info("[{}]将任务[{}]转交给[{}]处理", userId, task.getId(), params.getTargetUser());
    }

    /**
     * 取消流程处理
     * @param instanceId 流程实例ID
     * @param nodeId 当前流程所处节点ID
     */
    private void doCancelProcess(String instanceId, String nodeId) {
        List<Execution> executions = runtimeService.createExecutionQuery()
                .parentId(instanceId)
                .onlyChildExecutions().list();
        //强制流程指向驳回
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(instanceId)
                .moveActivityIdTo(nodeId, "cancel-end")
                .moveExecutionsToSingleActivityId(executions.stream().map(Execution::getId)
                        .collect(Collectors.toList()), "cancel-end")
                .changeState();
    }

    /**
     * 向上遍历加载用户任务审批节点
     * @param nowNode 当前节点
     * @param list 审批节点集合
     */
    private void loadBeforeSerialUserTaskNode(FlowNode nowNode, Set<String> list){
        //按串行向上遍历
        List<SequenceFlow> incomingFlows = nowNode.getIncomingFlows();
        if (incomingFlows.size() == 1){
            FlowElement beforeNode = incomingFlows.get(0).getSourceFlowElement();
            if (beforeNode instanceof UserTask){
                list.add(beforeNode.getId());
                loadBeforeSerialUserTaskNode((FlowNode) beforeNode, list);
            }else if (beforeNode instanceof ParallelGateway || beforeNode instanceof InclusiveGateway){
                //碰到并行和包容网关，判断是不是合流点，是就绕过继续往上，不是就截止
                if (((Gateway) beforeNode).getOutgoingFlows().size() == 1){
                    //只有一条出线，那么就是合流点
                    SequenceFlow sequenceFlow = ((Gateway) beforeNode).getIncomingFlows().get(0);
                    FlowElement gateway = getStartGateway(sequenceFlow.getSourceFlowElement(), beforeNode instanceof ParallelGateway);
                    if (Objects.nonNull(gateway)){
                        loadBeforeSerialUserTaskNode((FlowNode)gateway, list);
                    }
                }
            } else if (beforeNode instanceof ExclusiveGateway) {
                //碰到排他网关，里面每一条分支都要遍历
                ((ExclusiveGateway) beforeNode).getIncomingFlows().forEach(node -> {
                    loadBeforeSerialUserTaskNode((FlowNode) beforeNode, list);
                });
            }
        }
    }

    /**
     * 获取开始的并行/包容网关节点
     * @param node 节点
     * @param isPar 是否为并行网关
     * @return 开始的网关
     */
    private FlowElement getStartGateway(FlowElement node, Boolean isPar){
        if ((isPar && node instanceof ParallelGateway) || (!isPar && node instanceof InclusiveGateway)){
            //找到了对应的网关起始节点
            return node;
        }
        FlowNode flowNode = (FlowNode) node;
        return getStartGateway(flowNode.getIncomingFlows().get(0).getSourceFlowElement(), isPar);
    }
}
